/*      > C.Bitset - Bitset data type */

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "bitset.h"

#ifdef test
#include <stdio.h>
#endif

/* Return values from functions */

#define OK      1
#define ERR     0

#define CHAR_BIT_SHIFT  3
#define CHAR_BIT_MASK   0x07

/* General component routines */

bitset bset_new (int bits)
{
        bitset b;
        int bytes = ( bits >> CHAR_BIT_SHIFT )
                  + ( ( bits & CHAR_BIT_MASK ) ? 1 : 0 );

        /* Create a zero-filled bitmap (all unset) */

        b = calloc( sizeof(struct bitset) - 1 + bytes, 1 );

        if ( b == NULL )
                return NULL;

        b->bits = bits;
        b->bytes = bytes;

        return b;
}

void bset_free (bitset b)
{
        free(b);
}

void bset_clear (bitset b)
{
        memset(b->bitmap,0,b->bytes);
}

int bset_copy (bitset b1, bitset b2)
{
        if ( b1->bits != b2->bits )
                return ERR;

        memcpy(b1->bitmap,b2->bitmap,b1->bytes);

        return OK;
}

int bset_equal (bitset b1, bitset b2)
{
        if ( b1->bits != b2->bits )
                return 0;

        return ( memcmp(b1->bitmap,b2->bitmap,b1->bytes) );
}

int bset_empty (bitset b)
{
        int i;

        for ( i = 0; i < b->bytes; ++i )
                if ( b->bitmap[i] != 0 )
                        return 0;

        return 1;
}

int bset_size (bitset b)
{
        int i;
        unsigned char byte;
        int count = 0;

        for ( i = 0; i < b->bytes; ++i )
        {
                byte = b->bitmap[i];
                while ( byte != 0 )
                {
                        if ( ( byte & 1 ) != 0 )
                                ++count;
                        byte >>= 1;
                }
        }

        return count;
}

int bset_iterate (bitset b, int (*process)(int))
{
        int i;
        int ret;
        unsigned char byte;
        int current;

        for ( i = 0; i < b->bytes; ++i )
        {
                current = i << CHAR_BIT_SHIFT;
                byte = b->bitmap[i];
                while ( byte != 0 )
                {
                        if ( ( byte & 1 ) != 0 )
                        {
                                ret = (*process)(current);

                                /* Non-zero => stop processing here */
                                /* Negative => Abnormal (error) termination */

                                if ( ret != 0 )
                                        return ( ret >= 0 );
                        }

                        ++current;
                        byte >>= 1;
                }
        }

        return OK;
}

/* Bitset-specific routines */

int bset_set (bitset b, int bit)
{
        if ( bit >= b->bits )
                return ERR;

        b->bitmap [bit >> CHAR_BIT_SHIFT ] |= 1 << (bit & CHAR_BIT_MASK);

        return OK;
}

int bset_clr (bitset b, int bit)
{
        if ( bit >= b->bits )
                return ERR;

        b->bitmap [bit >> CHAR_BIT_SHIFT ] &= ~(1 << (bit & CHAR_BIT_MASK));

        return OK;
}

int bset_invert (bitset b, int bit)
{
        if ( bit >= b->bits )
                return ERR;

        b->bitmap [bit >> CHAR_BIT_SHIFT ] ^= 1 << (bit & CHAR_BIT_MASK);

        return OK;
}

int bset_test (bitset b, int bit)
{
        int res;

        if ( bit >= b->bits )
                return 0;

        res = b->bitmap [ bit >> CHAR_BIT_SHIFT ]
            & ( 1 << (bit & CHAR_BIT_MASK) );

        return res;
}

/*---------------------------------------------------------------------------*/

#ifdef test
int print (int i)
{
        printf("%d ",i);
        return STATUS_CONTINUE;
}

void bset_dump (bitset b)
{
        printf("Bitset: ");
        bset_iterate(b,print);
        putchar('\n');
}
#endif

/*---------------------------------------------------------------------------*/

#ifdef test

#define BUFLEN 255

int main (void)
{
        char buf[BUFLEN];
        int i, j, num;
        bitset b[10];

        for ( i = 0; i < 10; ++i )
                b[i] = bset_new(255);

        for ( ; ; )
        {
                printf(">");
                fgets(buf,BUFLEN,stdin);

                if ( buf[0] == '\n' || buf[0] == '\0' )
                        continue;
                else if ( sscanf(buf,"clear %1d",&i) == 1 )
                        bset_clear(b[i]);
                else if ( sscanf(buf,"copy %1d %1d",&i,&j) == 2 )
                        bset_copy(b[i],b[j]);
                else if ( sscanf(buf,"equal %1d %1d",&i,&j) == 2 )
                        printf("%s\n",(bset_equal(b[i],b[j]) ? "yes" : "no"));
                else if ( sscanf(buf,"empty %1d",&i) == 1 )
                        printf("%s\n",(bset_empty(b[i]) ? "yes" : "no"));
                else if ( sscanf(buf,"size %1d",&i) == 1 )
                        printf("%d\n",bset_size(b[i]));
                else if ( sscanf(buf,"dump %1d",&i) == 1 )
                        bset_dump(b[i]);
                else if ( sscanf(buf,"set %1d %d",&i,&num) == 2 )
                        bset_set(b[i],num);
                else if ( sscanf(buf,"clr %1d %d",&i,&num) == 2 )
                        bset_clr(b[i],num);
                else if ( sscanf(buf,"invert %1d %d",&i,&num) == 2 )
                        bset_invert(b[i],num);
                else if ( sscanf(buf,"test %1d %d",&i,&num) == 2 )
                        printf("%s\n",( bset_test(b[i],num) ? "yes" : "no" ));
                else if ( strncmp(buf,"quit",4) == 0 )
                        break;
                else
                        printf("Mistake\n");
        }

        printf("Deleting b[0-9]\n");
        for ( i = 0; i < 10; ++i )
                bset_free(b[i]);

        return 0;
}

#endif
